package com.bigflexlib.ds.op
{
	import flash.utils.Dictionary;
	
	import com.bigflexlib.ds.DataStructureEvent;
	import com.bigflexlib.ds.container.ContainerDiff;
	import com.bigflexlib.ds.container.IRContainer;
	import com.bigflexlib.ds.container.IWContainer;










	public class AbstractContainerMappingOp extends AbstractContainerOp
	{


		private var oContainer:IWContainer;
		
		
		private var iMappingFunction:Function;
		
		
		private var iCacheMappingResults:Boolean;
		

		private var cache:Dictionary;




		/**
		 * 
		 * @param iContainer
		 * 
		 * @param oContainer
		 * 
		 * @param iMappingFunction
		 * 
		 * @param iCacheMappingResults
		 * 		If set to true, an internal cache ( dictionary with weak keys ) 
		 * 		will keep a copy of the mapping result for each element indefinitely.
		 * 		Otherwise, the cache entries will not outlive the presence of the 
		 * 		element on the original container
		 * 
		 */
		public function AbstractContainerMappingOp( iContainer:IRContainer, oContainer:IWContainer, iMappingFunction:Function = null, iCacheMappingResults:Boolean = false )
		{
			
			
			super(iContainer);
			
			this.oContainer = oContainer;
			
			this.iMappingFunction = iMappingFunction;
			
			this.iCacheMappingResults = iCacheMappingResults;
			
			cache = new Dictionary( true );
			
			
			batch( );
			
		}
		
		

		private function batch( ):void
		{
		
			oContainer.autoUpdateFlag.disable( );
		
				iContainer.walk( walker );
							
			oContainer.autoUpdateFlag.enable( );
		
		}		
		
				
		
		private function map( e:* ):*
		{
			
			if ( iMappingFunction == null ) // pass-thru ( this is used to sync datastructures... a common scenario )
			{
				return e;
			}
			
			
			if ( cache[ e ] === undefined )
			{
				cache[ e ] = iMappingFunction.call( null, e );	
			}
			
			return cache[ e ];
			
		}
		


		override public function dispose( ):void
		{
			
			super.dispose();
			oContainer = null;
			iMappingFunction = null;
			cache = null;
		
		}




		


		override protected function iContainer_change( event:DataStructureEvent ):void
		{
			oContainer.autoUpdateFlag.disable( );

				( event.diff as ContainerDiff ).walk( walker );	
			
			oContainer.autoUpdateFlag.enable( );
								
		}
		
		
		
		
		private function walker( e:*, m:int ):void
		{

			$m( e, m );			
		
		}
		
		
		
		private function $m( e:*, m:int ):void
		{
		
			this.m( map( e ), m );
			
			
			if ( ! iCacheMappingResults ) // the cache must not outlive the presence of the element on the original array
			{
			
				if ( m < 0 )
					if ( ! iContainer.contains( e ) )	// e is no longer present, clear the cache for e
						delete cache[ e ];
			
			}
			
		
		} 
		
		
		
		
		// abstract
		protected function m( e:*, m:int ):void
		{
		
		}
		
		
		
		
		
	}
}